package com.android.assistivetouch;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.Service;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;

public class AssistiveTouchService extends Service {

    private WindowManager mWindowManager;
    // 辅助触控原点视图
    private ImageButton mAssistiveTouchView;
    // 辅助触控视图布局参数
    private WindowManager.LayoutParams mAssistiveTouchLp;
    private Handler mHandler;

    // 屏幕宽度
    private int mScreenWidth;
    // 屏幕高度
    private int mScreenHeight;
    // 辅助触控视图宽度
    private int mAssistiveTouchWidth;
    // 辅助触控视图高度
    private int mAssistiveTouchHeight;
    // 辅助触控视图水平位置
    private int mAssistiveTouchX;
    // 辅助触控视图垂直位置
    private int mAssistiveTouchY;
    // 当前触摸位置距离辅助触控视图原点位置X轴偏移值
    private int mXOffset;
    // 当前触摸位置距离辅助触控视图原点位置Y轴偏移值
    private int mYOffset;
    // 当前触摸视图的手指ID
    private int mPointerId;
    // 透明度动画时间
    private int mAlphaAnimationTime;
    // 透明度动画延迟时间
    private int mAlphaAnimationDelayTime;
    // 辅助触控视图透明度
    private float mAssistiveTouchViewAlpha;
    // 辅助触控功能是否开启
    private boolean isStarted = false;
    // 当前是否正在执行动画
    private boolean isAnimationing = false;
    // 中断触摸事件标志
    private boolean isInterruptTouchEvent = false;
    // 是否是点击事件
    private boolean isClickEvent = false;

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mHandler = new Handler();
        // 获取屏幕尺寸
        DisplayMetrics dm = getResources().getDisplayMetrics();
        mScreenWidth = dm.widthPixels;
        mScreenHeight = dm.heightPixels;
        Log.d(this, "onCreate=>screen width: " + mScreenWidth + ", screen height: " + mScreenHeight);
        // 计算辅助触控视图初始位置
        mAssistiveTouchWidth = dip2px(72);
        mAssistiveTouchHeight = dip2px(72);
        mAssistiveTouchX = mScreenWidth - mAssistiveTouchWidth;
        mAssistiveTouchY = (mScreenHeight - mAssistiveTouchHeight) / 2;
        mAssistiveTouchViewAlpha = 0.2f;
        mAlphaAnimationTime = 500;
        mAlphaAnimationDelayTime = 5000;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 移除辅助触控视图
        removeAssistiveTouchView();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // 设备参数改变时，更新屏幕尺寸信息
        DisplayMetrics dm = getResources().getDisplayMetrics();
        mScreenWidth = dm.widthPixels;
        mScreenHeight = dm.heightPixels;
    }

    /**
     * 添加辅助触控视图到界面中
     *
     * @return 添加成功返回 true，否则返回 false
     */
    private boolean addAssistiveTouchView() {
        try {
            if (mAssistiveTouchView == null) {
                mAssistiveTouchView = new ImageButton(this);
                mAssistiveTouchView.setImageResource(R.drawable.ic_assistive_touch);
                ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(mAssistiveTouchWidth, mAssistiveTouchHeight);
                mAssistiveTouchView.setLayoutParams(params);
                mAssistiveTouchView.setClickable(true);
                mAssistiveTouchView.setOnTouchListener(mAssistiveTouchViewListener);
                mAssistiveTouchView.setOnClickListener(mAssistiveTouchViewClickListener);
                mAssistiveTouchView.setAlpha(mAssistiveTouchViewAlpha);
                mAssistiveTouchLp = new WindowManager.LayoutParams();
                mAssistiveTouchLp.gravity = Gravity.LEFT | Gravity.TOP;
                mAssistiveTouchLp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
                mAssistiveTouchLp.format = PixelFormat.RGBA_8888;
                mAssistiveTouchLp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                mAssistiveTouchLp.x = mAssistiveTouchX;
                mAssistiveTouchLp.y = mAssistiveTouchY;
                mAssistiveTouchLp.width = mAssistiveTouchWidth;
                mAssistiveTouchLp.height = mAssistiveTouchHeight;
                mWindowManager.addView(mAssistiveTouchView, mAssistiveTouchLp);
            }
            return true;
        } catch (Exception e) {
            Log.e(this, "addAssistiveTouchView=>error: ", e);
            // 添加失败，重置视图对象为 null
            mAssistiveTouchView = null;
            return false;
        }
    }

    /**
     * 移除辅助触控视图
     *
     * @return 移除成功返回 true，否则返回 false
     */
    private boolean removeAssistiveTouchView() {
        try {
            if (mAssistiveTouchView != null) {
                mWindowManager.removeViewImmediate(mAssistiveTouchView);
                mAssistiveTouchView = null;
            }
            return true;
        } catch (Exception e) {
            Log.e(this, "addAssistiveTouchView=>error: ", e);
            return false;
        }
    }

    /**
     * 通过触摸位置更新辅助触控视图的位置
     *
     * @param event 触摸事件对象
     */
    private void updateAssistiveTouchViewPosition(MotionEvent event) {
        Log.d(this, "updateAssistiveTouchViewPosition=>x: " + Math.round(event.getRawX()) + ", y: " + Math.round(event.getRawY()));
        /*
         * 由于触摸点的位置不一定在视图的原点位置，所以需要减去触摸点位置的偏差，
         * 避免移动视图时出现跳动问题
         */
        mAssistiveTouchLp.x = Math.round(event.getRawX()) - mXOffset;
        mAssistiveTouchLp.y = Math.round(event.getRawY()) - mYOffset;
        mWindowManager.updateViewLayout(mAssistiveTouchView, mAssistiveTouchLp);
    }

    /**
     * 当接收到{code MotionEvent.ACTION_UP}事件后，更新辅助触控视图位置到屏幕边缘上
     *
     * @param event 触摸事件对象
     */
    private void updateAssistiveTouchViewFinalPosition(MotionEvent event) {
        int x = 0;
        int y = 0;
        int pointX = Math.round(event.getRawX());
        int pointY = Math.round(event.getRawY());
        if (pointX < 0) {
            pointX = 0;
        }
        if (pointX > mScreenWidth) {
            pointX = mScreenWidth;
        }
        if (pointY < 0) {
            pointY = 0;
        }
        if (pointY > mScreenHeight) {
            pointY = mScreenHeight;
        }
        int offLeft = pointX;
        int offRight = mScreenWidth - pointX;
        int offTop = pointY;
        int offBottom = mScreenHeight - pointY;
        Log.d(this, "updateAssistiveTouchViewFinalPosition=>left: " + offLeft + ", right: " + offRight + ", top: " + offTop + ", bottom: " + offBottom);
        if (offLeft < offRight) {
            // 视图在屏幕左边
           if (offTop < offBottom) {
               // 视图在屏幕左上角
               if (offLeft < offTop) {
                   y = offTop - mYOffset;
               } else {
                   x = offLeft - mXOffset;
               }
           } else {
               // 视图在屏幕左下角
               if (offLeft < offBottom) {
                   y = mScreenHeight - offBottom - mYOffset;
               } else {
                   x = offLeft - mXOffset;
                   y = mScreenHeight - mAssistiveTouchHeight;
               }
           }
        } else {
            // 视图在屏幕右边
            if (offTop < offBottom) {
                // 视图在屏幕右上角
                if (offRight < offTop) {
                    x = mScreenWidth - mAssistiveTouchWidth;
                    y = offTop - mYOffset;
                } else {
                    x = mScreenWidth - offRight - mXOffset;
                }
            } else {
                // 视图在屏幕右下角
                if (offRight < offBottom) {
                    x = mScreenWidth - mAssistiveTouchWidth;
                    y = mScreenHeight - offBottom - mYOffset;
                } else {
                    x = mScreenWidth - offRight - mXOffset;
                    y = mScreenHeight - mAssistiveTouchHeight;
                }
            }
        }
        mAssistiveTouchLp.x = x;
        mAssistiveTouchLp.y = y;
        mWindowManager.updateViewLayout(mAssistiveTouchView, mAssistiveTouchLp);
    }

    /**
     * dp 转 px
     * @param dpValue dp 值
     * @return 返回 px 值
     */
    public int dip2px(float dpValue) {
        float scale = getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    // 辅助触控视图点击事件处理对象
    private final View.OnClickListener mAssistiveTouchViewClickListener = v -> {
        Log.d(this, "AssistiveTouchViewClickListener()...");
    };

    /**
     * 辅助触控视图触摸事件处理对象
     */
    private final View.OnTouchListener mAssistiveTouchViewListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // 如果透明度动画还没有开始，就取消掉
            mHandler.removeCallbacks(mAlphaAnimaRunnable);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // 动画期间不处理触摸事件
                    if (isAnimationing) {
                        isInterruptTouchEvent = true;
                        return false;
                    }
                    // 记录当前触摸视图的手指ID
                    mPointerId = event.getPointerId(event.getActionIndex());
                    Log.d(AssistiveTouchService.this, "onTouchEvent=>event: ACTION_DOWN " + event.getPointerCount());
                    isClickEvent = true;
                    mXOffset = Math.round(event.getRawX()) - mAssistiveTouchLp.x;
                    mYOffset = Math.round(event.getRawY()) - mAssistiveTouchLp.y;
                    mAssistiveTouchView.setAlpha(1.0f);
                    break;

                case MotionEvent.ACTION_MOVE:
                    // 动画期间不处理触摸事件
                    if (isInterruptTouchEvent) {
                        return false;
                    }
                    for (int i = 0; i < event.getPointerCount(); i++) {
                        int id = event.getPointerId(i);
                        // 只处理触摸视图的手指事件
                        if (id == mPointerId) {
                            // 因为点击视图后马上抬起不会触发 ACTION_MOVE 事件，
                            // 因此可以通过该事件来判断是否是点击事件
                            if (isClickEvent) {
                                isClickEvent = false;
                            }
                            updateAssistiveTouchViewPosition(event);
                        }
                    }

                    break;

                case MotionEvent.ACTION_POINTER_UP:
                case MotionEvent.ACTION_UP:
                    Log.d(AssistiveTouchService.this, "onTouchEvent=>event: ACTION_UP");
                    // 动画期间不处理触摸事件
                    if (isInterruptTouchEvent) {
                        isInterruptTouchEvent = false;
                        return false;
                    }
                    // 当多个手指按在屏幕上时，只通过按住视图的手指处理手指抬起事件，其他手指事件忽略
                    for (int i = 0; i < event.getPointerCount(); i++) {
                        int id = event.getPointerId(i);
                        if (id == mPointerId) {
                            updateAssistiveTouchViewFinalPosition(event);
                            if (isClickEvent) {
                                mAssistiveTouchView.performClick();
                            } else {
                                mHandler.postDelayed(mAlphaAnimaRunnable, mAlphaAnimationDelayTime);
                            }
                        }
                    }
                    break;

                case MotionEvent.ACTION_OUTSIDE:
                    Log.d(AssistiveTouchService.this, "onTouchEvent=>event: ACTION_OUTSIDE");
                    // 动画期间不处理触摸事件
                    if (isInterruptTouchEvent) {
                        isInterruptTouchEvent = false;
                        return false;
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    Log.d(AssistiveTouchService.this, "onTouchEvent=>event: ACTION_CANCEL");
                    // 动画期间不处理触摸事件
                    if (isInterruptTouchEvent) {
                        isInterruptTouchEvent = false;
                        return false;
                    }
                    break;
            }
            return true;
        }
    };

    /**
     * 设置辅助触控视图透明度 Runnable
     */
    private final Runnable mAlphaAnimaRunnable = new Runnable() {
        @Override
        public void run() {
            if (mAssistiveTouchView != null) {
                // 辅助触控视图透明度动画
                ObjectAnimator mAlphaAnima = ObjectAnimator.ofFloat(mAssistiveTouchView,
                        "alpha", 1.0f, mAssistiveTouchViewAlpha, mAssistiveTouchViewAlpha);
                mAlphaAnima.setDuration(mAlphaAnimationTime);
                mAlphaAnima.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        isAnimationing = false;
                    }

                    @Override
                    public void onAnimationStart(Animator animation) {
                        isAnimationing = true;
                    }
                });
                mAlphaAnima.start();
            }
        }
    };

    /**
     * Service 的 Binder 对象
     */
    private final IAssistiveTouchInterface.Stub mBinder = new IAssistiveTouchInterface.Stub() {
        @Override
        public void setAssitiveTouchState(boolean on) throws RemoteException {
            Log.d(AssistiveTouchService.this, "setAssitiveTouchState=>on: " + on);
            if (on) {
                if (addAssistiveTouchView()) {
                    isStarted = true;
                } else {
                    throw new RemoteException("无法打开辅助控制功能");
                }
            } else {
                if (removeAssistiveTouchView()) {
                    isStarted = false;
                } else {
                    throw new RemoteException("无法关闭辅助控制功能");
                }
            }
        }

        @Override
        public boolean getAssitiveTouchState() {
            Log.d(AssistiveTouchService.this, "getAssitiveTouchState=>state: " + isStarted);
            return isStarted;
        }
    };
}